package top.rzhsky.server;

import top.rzhsky.bean.Message;
import top.rzhsky.bean.MessageRecord;
import top.rzhsky.bean.Type;
import top.rzhsky.bean.User;
import top.rzhsky.dao.Dao;
import top.rzhsky.dao.impl.UserDao;
import top.rzhsky.util.QQCreate;

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.*;
import java.util.logging.Logger;

/**
 * @author rzh
 * time 2018-11-02 17:19
 */
public class Server {
    private static final HashMap<String, User> names = new HashMap<>(); // 保存在线的用户
    private static HashSet<ObjectOutputStream> writers = new HashSet<>(); // 保存输出流
    private static HashMap<String, ObjectOutputStream> writerMap = new HashMap<>(); // 保存输出流

    private static HashSet<User> hasRegister = new HashSet<>(); // 保存已注册的用户
    private static Map<String, List<MessageRecord>> listMap = new HashMap<>(); // 保存信息记录

    private static Logger logger = Logger.getLogger("Server");

    public static void main(String[] args) throws Exception {
        ServerSocket listener = new ServerSocket(Config.port);

        try {
            while (true) {
                new Handler(listener.accept()).start();
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            listener.close();
        }
    }

    /**
     * 服务端处理器
     */
    private static class Handler extends Thread {
        private String account; // 账号
        private Socket socket;
        private User user; //用户
        private ObjectInputStream input;
        private ObjectOutputStream output;
        private Dao dao = new UserDao();

        public Handler(Socket socket) {
            this.socket = socket;
        }

        public void run() {
            try {
                input = new ObjectInputStream(socket.getInputStream());
                output = new ObjectOutputStream(socket.getOutputStream());

                //将每个socket的输出流保存在map集合中
                writers.add(output);

                hasRegister = dao.listAllUserName(); //获取到所有注册到的信息

                while (socket.isConnected()) {
                    //读取客户端发过来的消息
                    Message inputMsg = (Message) input.readObject();

                    if (inputMsg != null) {
                        if (inputMsg.getFrom().getAccount() != null && !writerMap.containsKey(inputMsg.getFrom().getAccount())) {
                            //将用户与对应的输出流保存在map中
                            writerMap.put(inputMsg.getFrom().getAccount(), output);
                        }
                        switch (inputMsg.getType()) {
                            case REGISTER:
                                register(inputMsg);
                                break;
                            case LOGIN:
                                login(inputMsg);
                                break;
                            case CHAT:
                                writeToOne(inputMsg);
                                break;
                            case GROUPCHAT:
                                writerToAll(inputMsg);
                                break;
                            case SENDFILE:
                                sendFileToOne(inputMsg);
                                break;
                        }
                    }
                }
            } catch (Exception e) {
                logger.warning(e.getMessage());
            } finally {
                //对用户下线进行处理
                closeConnection();
            }
        }

        private void login(Message inputMsg) {
            User user = dao.login(inputMsg);
            System.out.println(user);
            Message msg = new Message();
            try {
                if (user != null) {
                    if (names.containsKey(user.getAccount())) {
                        logger.warning("用户" + user.getAccount() + "已经登录");
                        msg.setType(Type.HASCONNECTED);
                        output.writeObject(msg);
                    } else {
                        logger.info(user.toString());
                        names.put(user.getAccount(), user);

                        account = user.getAccount();

                        msg.setFrom(user);
                        msg.setType(Type.CONNECTED);
                        msg.setHasRegister(hasRegister);
                        msg.setUserList(names);
                        output.writeObject(msg);

                        Message message = new Message();
                        message.setType(Type.LOGINUSERLIST);
                        write(message);

                        logger.info(msg.getHasRegister().toString());
                        logger.info("account " + account + " on line");
                    }
                } else {
                    msg.setType(Type.LOGINFAIL);
                    output.writeObject(msg);
                }
                logger.info("userList:" + names);
            } catch (IOException e) {
                logger.warning(e.getMessage());
            }

//            String account = inputMsg.getFrom().getAccount();
//            String password = inputMsg.getFrom().getPassword();
//            try {
//                File[] files = new File("files").listFiles();
//                boolean isExist = false;
//                if (files != null && files.length != 0) {
//                    for (File file : files) {
//                        if (file.getName().equals(account + ".dat")) {
//                            isExist = true;
//                            break;
//                        }
//                    }
//                }
//
//                if (isExist) {
//                    ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("files/" + account + ".dat"));
//                    Message msg = (Message) objectInputStream.readObject();
//                    logger.info(msg.toString());
//
//                    if (names.containsKey(msg.getFrom().getAccount())) {
//                        msg = new Message();
//                        msg.setType(Type.HASCONNECTED);
//                        output.writeObject(msg);
//                    }
//
//                    if (password.equals(msg.getFrom().getPassword())) {
//                        user = msg.getFrom();
//                        this.account = user.getAccount();
//                        names.put(this.account, user);
//                        //users.add(user);
//                        msg.setType(Type.CONNECTED);
//                        //msg.setUser(users);
//                        msg.setHasRegister(hasRegister);
//                        msg.setUserList(names);
//                        output.writeObject(msg);
//
//                        Message message = new Message();
//                        message.setType(Type.LOGINUSERLIST);
//                        write(message);
//                        logger.info(msg.getHasRegister().toString());
//                        logger.info("account " + account + " on line");
//                    } else {
//                        msg = new Message();
//                        msg.setType(Type.LOGINFAIL);
//                        output.writeObject(msg);
//                    }
//                } else {
//                    Message msg = new Message();
//                    msg.setType(Type.LOGINFAIL);
//                    output.writeObject(msg);
//                }
//
//            } catch (IOException e) {
//                logger.warning(e.getMessage());
//            } catch (ClassNotFoundException e) {
//                logger.warning("文件不存在");
//            }
        }

        private void register(Message inputMsg) {
            logger.info("register() method Enter");

            String registerAccount = null;
            try {
                User user = dao.register(inputMsg);
                registerAccount = user.getAccount();

                Message msg = new Message();
                msg.setFrom(user);
                msg.setType(Type.REGISTER);
                output.writeObject(inputMsg);
            } catch (IOException e) {
                logger.warning(e.getMessage());
            }
//            ObjectOutputStream objectOutputStream = null;
//            File file;
//            String registerAccount = null;
//
//            File dir = new File("files");
//            if (!dir.exists()) {
//                dir.mkdir();
//            }
//            try {
//                do {
//                    registerAccount = QQCreate.createQQ();
//                    file = new File("files/" + registerAccount + ".dat");
//                } while (file.exists());
//
//                inputMsg.getFrom().setAccount(registerAccount);
//
//                objectOutputStream = new ObjectOutputStream(new FileOutputStream(file));
//                objectOutputStream.writeObject(inputMsg);
//                objectOutputStream.flush();
//
//                output.writeObject(inputMsg);
//                output.flush();
//            } catch (Exception e) {
//                if (objectOutputStream != null) {
//                    try {
//                        objectOutputStream.close();
//                    } catch (IOException e1) {
//                        e1.printStackTrace();
//                    }
//                }
//                e.printStackTrace();
//            }
            logger.info("register account is " + registerAccount + ", password is " + inputMsg.getFrom().getPassword());
            logger.info("register() method Enter");
        }

        private void sendFileToOne(Message msg) throws IOException {
            String account = msg.getTo().getAccount();
            if (writerMap.containsKey(account)) {
                ObjectOutputStream writer = writerMap.get(account);
                writer.writeObject(msg);
            } else {
                ObjectOutputStream writer = writerMap.get(msg.getFrom().getAccount());
                Message message = new Message();
                message.setType(Type.SENDFAIL);
                writer.writeObject(message);
            }
        }

        private void writerToAll(Message msg) throws IOException {
            if (msg.getContext() != null && !msg.getContext().equals("")) {
                String key = "groupChat";
                if (listMap.containsKey(key)) {
                    List<MessageRecord> messageRecords = listMap.get(key);
                    messageRecords.add(new MessageRecord(msg.getFrom(), null, msg.getContext(), new Date().toLocaleString()));
                } else {
                    List<MessageRecord> messageRecords = new ArrayList<>();
                    messageRecords.add(new MessageRecord(msg.getFrom(), null, msg.getContext(), new Date().toLocaleString()));
                    listMap.put(key, messageRecords);
                }
            }

            logger.info(msg.getFrom().getNickName() + "(" + msg.getFrom().getPassword() + ")" + " to chatRoom " +
                    ": " + msg.getContext());

            msg.setListMap(listMap);
            msg.setUserList(names);
            msg.setHasRegister(hasRegister);

            if (msg.getContext() != null && !msg.getContext().equals("")){
                for (ObjectOutputStream writer : writers) {
                    writer.writeObject(msg);
                    writer.reset();
                }
            }else {
                ObjectOutputStream writer = writerMap.get(msg.getFrom().getAccount());
                writer.writeObject(msg);
                writer.reset();
            }
        }

        private void writeToOne(Message msg) throws IOException {
            User fromUser = msg.getFrom();
            User toUser = msg.getTo();

            ObjectOutputStream writerFrom = writerMap.get(fromUser.getAccount());

            saveMessage(msg, fromUser, toUser);
            msg.setListMap(listMap);
            msg.setUserList(names);
            msg.setHasRegister(hasRegister);
            writerFrom.writeObject(msg);
            writerFrom.reset();

            if (writerMap.containsKey(toUser.getAccount()) && msg.getContext() != null && !msg.getContext().equals("")) {

                ObjectOutputStream writerTo = writerMap.get(toUser.getAccount());
                writerTo.writeObject(msg);
                writerTo.reset();


                logger.info(fromUser.getNickName() + "(" + fromUser.getPassword() + ")" + " to " + toUser.getNickName() + "(" + toUser.getPassword() + ")" +
                        ": " + msg.getContext());
            }
        }

        private void saveMessage(Message msg, User fromUser, User toUser) {
            if (msg.getContext() != null && !msg.getContext().equals("")) {
                String key1 = fromUser.getAccount() + "-" + toUser.getAccount();
                String key2 = toUser.getAccount() + "-" + fromUser.getAccount();
                if (listMap.containsKey(key1) || listMap.containsKey(key2)) {
                    List<MessageRecord> messageRecords = listMap.containsKey(key1) ? listMap.get(key1) : listMap.get(key2);
                    messageRecords.add(new MessageRecord(fromUser, toUser, msg.getContext(), new Date().toLocaleString()));
                } else {
                    List<MessageRecord> messageRecords = new ArrayList<>();
                    messageRecords.add(new MessageRecord(fromUser, toUser, msg.getContext(), new Date().toLocaleString()));
                    listMap.put(key1, messageRecords);
                }
            }
        }


        private void write(Message msg) throws IOException {
            for (ObjectOutputStream writer : writers) {
                msg.setUserList(names);
                //msg.setUser(users);
                msg.setHasRegister(hasRegister);
                writer.writeObject(msg);
                writer.reset();
            }
        }

        private Message removeFromList() throws IOException {
            logger.info("removeFromList() method Enter");
            Message msg = new Message();
            msg.setType(Type.DISCONNECTED);
            write(msg);
            logger.info("removeFromList() method Exit");
            return msg;
        }



        private void closeConnection() {
            logger.info("closeConnections() method Enter");
            logger.info("HashMap names:" + names.size() + " writers:" + writers.size() + " usersList size:" + names.size());
            if (account != null) {
                names.remove(account);
                logger.info("User: " + account + " has been removed!");
            }

            if (output != null) {
                writers.remove(output);
                try {
                    writerMap.remove(user.getAccount());
                } catch (Exception ignored) {
                }
                logger.info("Writer object: " + user + " has been removed!");
            }
            if (input != null) {
                try {
                    input.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            try {
                removeFromList();
            } catch (Exception e) {
                e.printStackTrace();
            }
            logger.info("HashMap names:" + names.size() + " writers:" + writers.size() + " usersList size:" + names.size());
            logger.info("closeConnections() method Exit");
        }
    }
}
